package com.lx.constant.segment.query.join;

import com.lx.constant.consts.SqlFormatConstant;
import com.lx.constant.enums.GenericJoinTypeEnum;
import com.lx.constant.segment.Segment;
import com.lx.wrapper.AbstractWrapper;
import com.lx.wrapper.every.LambdaOnConditionWrapper;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.query.Query;

import java.util.Objects;

/**
 * JOIN 语句
 *
 * @author lx
 * @create 2023/2/24 16:02
 **/
@Getter
@Setter
public class JoinSegment<C1, C2> extends Segment {

    /**
     * Join关联类型
     */
    private GenericJoinTypeEnum joinType;

    /**
     * 左表实体Wrapper,仅用于获取右表表名及其别名
     */
    private AbstractWrapper<C1, ?> leftJoinWrapper;

    /**
     * 右表实体Wrapper,仅用于获取右表表名及其别名
     */
    private AbstractWrapper<C2, ?> rightJoinWrapper;

    /**
     * ON 关联条件 Wrapper
     */
    private volatile LambdaOnConditionWrapper onConditionWrapper;

    @Override
    public String convertSegmentToQL(AbstractWrapper parentWrapper, AbstractWrapper thisWrapper, boolean isHQL) {
        //获取别名. 1.获取左方Wrapper表别名(左方Wrapper中存在EntityClass时,由于JoinSegment创建时已经生成别名,需要优先从thisWrapper中尝试获取别名) 2.获取右方Wrapper的表别名(一定从thisWrapper中获取)
        String leftEntityTableAliasName = thisWrapper.getTableAliasName(false, leftJoinWrapper.getEntityClass()),
                rightEntityTableAliasName = thisWrapper.getTableAliasName(false, rightJoinWrapper.getEntityClass());

        //补全表别名,目的为:当存在表名后缀时,需要在别名上添加,防止多个相同实体重复命名
        leftEntityTableAliasName = leftJoinWrapper.getTableSuffix() == null ? leftEntityTableAliasName : leftEntityTableAliasName + leftJoinWrapper.getTableSuffix();
        rightEntityTableAliasName = rightJoinWrapper.getTableSuffix() == null ? rightEntityTableAliasName : rightEntityTableAliasName + rightJoinWrapper.getTableSuffix();

        //构建 join sql 数据
        StringBuilder sb = new StringBuilder(100).append(joinType.getKeyWord());
        sb.append(String.format(SqlFormatConstant.AS_FORMAT, rightJoinWrapper.inferAndGetTableName(isHQL), rightEntityTableAliasName));

        LambdaOnConditionWrapper onConditionWrapper = getOnConditionWrapper();
        if (onConditionWrapper != null) {
            //设置本次On条件中的左右表别名.
            onConditionWrapper.setTableAlias(leftJoinWrapper.getEntityClass(), leftEntityTableAliasName);
            onConditionWrapper.setTableAlias(rightJoinWrapper.getEntityClass(), rightEntityTableAliasName);

            //构建On条件 (此处无需传入 parentWrapper)
            sb.append(onConditionWrapper.getCompleteQL(isHQL));
        }
        return sb.toString();
    }

    @Override
    public void setHibernateParamByQuery(boolean isHQL, Query query) {
        LambdaOnConditionWrapper onConditionWrapper = getOnConditionWrapper();
        if (onConditionWrapper != null) {
            onConditionWrapper.setHibernateParamByQuery(isHQL, query);
        }
    }

    /**
     * 意义:
     * 1.相同左右表实体未设置表后缀的情况下,不允许重复设置左右表相同的实体对象
     * 2.左表 = 右表 且 右表 = 左表的情况也不允许
     *
     * @param t1
     * @return
     */
    @Override
    public boolean needReplace(Segment t1) {
        if (this == t1) {
            return true;
        }
        if (!(t1 instanceof JoinSegment)) {
            return false;
        }
        JoinSegment outerSegment = (JoinSegment) t1;

        AbstractWrapper outLeftJoinWrapper = outerSegment.getLeftJoinWrapper();
        AbstractWrapper outRightJoinWrapper = outerSegment.getRightJoinWrapper();
        AbstractWrapper thisLeftJoinWrapper = this.getLeftJoinWrapper();
        AbstractWrapper thisRightJoinWrapper = this.getRightJoinWrapper();

        //表名后缀
        String ols = null, ors = null, tls = null, trs = null;
        //实体类型
        Class<?> olc = null, orc = null, tlc = null, trc = null;

        //传入Segment 左方 实体及表后缀
        ols = outLeftJoinWrapper.getTableSuffix();
        olc = outLeftJoinWrapper.getEntityClass();

        //传入Segment 右方 实体及表后缀
        ors = outRightJoinWrapper.getTableSuffix();
        orc = outRightJoinWrapper.getEntityClass();

        //本Segment 左方 实体及表后缀
        tls = thisLeftJoinWrapper.getTableSuffix();
        tlc = thisLeftJoinWrapper.getEntityClass();
        //本Segment 右方 实体及表后缀
        trs = thisRightJoinWrapper.getTableSuffix();
        trc = thisRightJoinWrapper.getEntityClass();

        //1.左表与左表 相等 且 右表与右表 相等
        if (Objects.equals(ols, tls) && Objects.equals(olc, tlc)
                && Objects.equals(ors, trs) && Objects.equals(orc, trc)) {
            return true;
        }
        //2.左表与右表 相等 且 右表与左表 相等
        if (Objects.equals(ols, trs) && Objects.equals(olc, trc)
                && Objects.equals(ors, tls) && Objects.equals(orc, tlc)) {
            return true;
        }

        return false;
    }
}
